package main

import (
	"archive/tar"
	"archive/zip"
	"compress/gzip"
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"time"
)

const (
	uploadDir     = "uploads"
	outputDir     = "output"
	oemDir        = "output/out/oem"
	imageDir      = "output/image"
	downloadsDir  = "downloads"
	maxUploadSize = 100 * 1024 * 1024 // 100MB
)

func main() {
	// 创建必要的目录
	createDirectories()

	// 设置路由
	http.HandleFunc("/", handleHome)
	http.HandleFunc("/upload", handleUpload)
	http.HandleFunc("/download/", handleDownload)
	http.HandleFunc("/status", handleStatus)

	// 静态文件服务
	fs := http.FileServer(http.Dir("static"))
	http.Handle("/static/", http.StripPrefix("/static/", fs))

	fmt.Println("服务器启动在 http://localhost:8080")
	log.Fatal(http.ListenAndServe(":8080", nil))
}

func createDirectories() {
	dirs := []string{uploadDir, oemDir, imageDir, downloadsDir}
	for _, dir := range dirs {
		if err := os.MkdirAll(dir, 0755); err != nil {
			log.Fatal("创建目录失败:", err)
		}
	}
}

func handleHome(w http.ResponseWriter, r *http.Request) {
	if r.URL.Path != "/" {
		http.NotFound(w, r)
		return
	}

	html := `
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>OEM镜像生成器</title>
    <style>
        body {
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            max-width: 800px;
            margin: 0 auto;
            padding: 20px;
            background-color: #f5f5f5;
        }
        .container {
            background: white;
            padding: 30px;
            border-radius: 10px;
            box-shadow: 0 2px 10px rgba(0,0,0,0.1);
        }
        h1 {
            color: #333;
            text-align: center;
            margin-bottom: 30px;
        }
        .upload-area {
            border: 2px dashed #ddd;
            border-radius: 8px;
            padding: 40px;
            text-align: center;
            margin: 20px 0;
            transition: border-color 0.3s;
        }
        .upload-area:hover {
            border-color: #007bff;
        }
        .upload-area.dragover {
            border-color: #007bff;
            background-color: #f8f9fa;
        }
        #fileInput {
            display: none;
        }
        .btn {
            background-color: #007bff;
            color: white;
            padding: 12px 24px;
            border: none;
            border-radius: 5px;
            cursor: pointer;
            font-size: 16px;
            transition: background-color 0.3s;
        }
        .btn:hover {
            background-color: #0056b3;
        }
        .btn:disabled {
            background-color: #6c757d;
            cursor: not-allowed;
        }
        #progress {
            margin-top: 20px;
            display: none;
        }
        .progress-bar {
            width: 100%;
            height: 20px;
            background-color: #e9ecef;
            border-radius: 10px;
            overflow: hidden;
        }
        .progress-fill {
            height: 100%;
            background-color: #007bff;
            width: 0%;
            transition: width 0.3s;
        }
        #status {
            margin-top: 20px;
            padding: 15px;
            border-radius: 5px;
            display: none;
        }
        .status-success {
            background-color: #d4edda;
            color: #155724;
            border: 1px solid #c3e6cb;
        }
        .status-error {
            background-color: #f8d7da;
            color: #721c24;
            border: 1px solid #f5c6cb;
        }
        .download-link {
            display: inline-block;
            margin-top: 10px;
            padding: 10px 20px;
            background-color: #28a745;
            color: white;
            text-decoration: none;
            border-radius: 5px;
        }
        .download-link:hover {
            background-color: #218838;
        }
    </style>
</head>
<body>
    <div class="container">
        <h1>OEM镜像生成器</h1>
        
        <div class="upload-area" id="uploadArea">
            <p>点击选择文件或拖拽 app.tar 文件到此处</p>
            <input type="file" id="fileInput" accept=".tar">
            <button class="btn" onclick="document.getElementById('fileInput').click()">选择文件</button>
        </div>

        <div id="progress">
            <p>处理进度:</p>
            <div class="progress-bar">
                <div class="progress-fill" id="progressFill"></div>
            </div>
            <p id="progressText">准备中...</p>
        </div>

        <div id="status"></div>
    </div>

    <script>
        const uploadArea = document.getElementById('uploadArea');
        const fileInput = document.getElementById('fileInput');
        const progress = document.getElementById('progress');
        const progressFill = document.getElementById('progressFill');
        const progressText = document.getElementById('progressText');
        const status = document.getElementById('status');

        // 拖拽功能
        uploadArea.addEventListener('dragover', (e) => {
            e.preventDefault();
            uploadArea.classList.add('dragover');
        });

        uploadArea.addEventListener('dragleave', () => {
            uploadArea.classList.remove('dragover');
        });

        uploadArea.addEventListener('drop', (e) => {
            e.preventDefault();
            uploadArea.classList.remove('dragover');
            const files = e.dataTransfer.files;
            if (files.length > 0) {
                handleFile(files[0]);
            }
        });

        fileInput.addEventListener('change', (e) => {
            if (e.target.files.length > 0) {
                handleFile(e.target.files[0]);
            }
        });

        function handleFile(file) {
            if (!file.name.endsWith('.tar')) {
                showStatus('请选择 .tar 文件', 'error');
                return;
            }

            const formData = new FormData();
            formData.append('file', file);

            progress.style.display = 'block';
            progressFill.style.width = '0%';
            progressText.textContent = '上传文件中...';

            fetch('/upload', {
                method: 'POST',
                body: formData
            })
            .then(response => response.json())
            .then(data => {
                if (data.success) {
                    progressFill.style.width = '100%';
                    progressText.textContent = '处理完成!';
                    showStatus('文件处理成功! <a href="/download/' + data.filename + '" class="download-link">下载镜像包</a>', 'success');
                } else {
                    showStatus('处理失败: ' + data.error, 'error');
                }
            })
            .catch(error => {
                showStatus('上传失败: ' + error.message, 'error');
            });
        }

        function showStatus(message, type) {
            status.innerHTML = message;
            status.className = type === 'success' ? 'status-success' : 'status-error';
            status.style.display = 'block';
        }
    </script>
</body>
</html>`

	w.Header().Set("Content-Type", "text/html; charset=utf-8")
	w.Write([]byte(html))
}

func handleUpload(w http.ResponseWriter, r *http.Request) {
	if r.Method != "POST" {
		http.Error(w, "只支持POST请求", http.StatusMethodNotAllowed)
		return
	}

	// 限制文件大小
	r.ParseMultipartForm(maxUploadSize)
	file, header, err := r.FormFile("file")
	if err != nil {
		http.Error(w, "获取文件失败: "+err.Error(), http.StatusBadRequest)
		return
	}
	defer file.Close()

	// 检查文件类型
	if !strings.HasSuffix(header.Filename, ".tar") {
		http.Error(w, "只支持.tar文件", http.StatusBadRequest)
		return
	}

	// 创建临时文件
	tempFile, err := os.CreateTemp(uploadDir, "app-*.tar")
	if err != nil {
		http.Error(w, "创建临时文件失败: "+err.Error(), http.StatusInternalServerError)
		return
	}
	defer os.Remove(tempFile.Name())
	defer tempFile.Close()

	// 保存上传的文件
	_, err = io.Copy(tempFile, file)
	if err != nil {
		http.Error(w, "保存文件失败: "+err.Error(), http.StatusInternalServerError)
		return
	}

	// 处理文件
	filename, err := processFile(tempFile.Name())
	if err != nil {
		http.Error(w, "处理文件失败: "+err.Error(), http.StatusInternalServerError)
		return
	}

	// 返回成功响应
	w.Header().Set("Content-Type", "application/json")
	fmt.Fprintf(w, `{"success": true, "filename": "%s"}`, filename)
}

func processFile(tarPath string) (string, error) {
	// 1. 确保oem目录存在（不清除现有文件）
	if err := os.MkdirAll(oemDir, 0755); err != nil {
		return "", fmt.Errorf("创建oem目录失败: %v", err)
	}

	// 2. 解压tar文件（会覆盖现有文件）
	if err := extractTar(tarPath, oemDir); err != nil {
		return "", fmt.Errorf("解压文件失败: %v", err)
	}

	// 3. 执行oem.sh脚本
	if err := executeOemScript(); err != nil {
		return "", fmt.Errorf("执行oem.sh失败: %v", err)
	}

	// 4. 创建zip文件
	filename := fmt.Sprintf("image-%s.zip", time.Now().Format("20060102"))
	zipPath := filepath.Join(downloadsDir, filename)
	if err := createImageZip(imageDir, zipPath); err != nil {
		return "", fmt.Errorf("创建zip文件失败: %v", err)
	}

	return filename, nil
}

func extractTar(tarPath, destDir string) error {
	file, err := os.Open(tarPath)
	if err != nil {
		return err
	}
	defer file.Close()

	var reader io.Reader = file

	// 检查是否是gzip压缩的tar
	if strings.HasSuffix(tarPath, ".tar.gz") || strings.HasSuffix(tarPath, ".tgz") {
		gzReader, err := gzip.NewReader(file)
		if err != nil {
			return err
		}
		defer gzReader.Close()
		reader = gzReader
	}

	tarReader := tar.NewReader(reader)

	for {
		header, err := tarReader.Next()
		if err == io.EOF {
			break
		}
		if err != nil {
			return err
		}

		target := filepath.Join(destDir, header.Name)

		switch header.Typeflag {
		case tar.TypeDir:
			if err := os.MkdirAll(target, 0755); err != nil {
				return err
			}
		case tar.TypeReg:
			if err := os.MkdirAll(filepath.Dir(target), 0755); err != nil {
				return err
			}
			file, err := os.Create(target)
			if err != nil {
				return err
			}
			if _, err := io.Copy(file, tarReader); err != nil {
				file.Close()
				return err
			}
			file.Close()
			
			// 检查是否为可执行文件并添加执行权限
			if isExecutable(header.Name) {
				if err := os.Chmod(target, 0755); err != nil {
					return fmt.Errorf("设置执行权限失败 %s: %v", target, err)
				}
			}
		}
	}

	return nil
}

// 检查文件是否为可执行文件
func isExecutable(filename string) bool {
	// 没有后缀名的文件就是可执行程序
	ext := filepath.Ext(filename)
	return ext == ""
}

func executeOemScript() error {
	cmd := exec.Command("bash", "oem.sh")
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	return cmd.Run()
}

func createImageZip(imageDir, zipPath string) error {
	zipfile, err := os.Create(zipPath)
	if err != nil {
		return err
	}
	defer zipfile.Close()

	zipWriter := zip.NewWriter(zipfile)
	defer zipWriter.Close()

	return filepath.Walk(imageDir, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}

		if info.IsDir() {
			return nil
		}

		relPath, err := filepath.Rel(imageDir, path)
		if err != nil {
			return err
		}

		file, err := os.Open(path)
		if err != nil {
			return err
		}
		defer file.Close()

		zipEntry, err := zipWriter.Create(relPath)
		if err != nil {
			return err
		}

		_, err = io.Copy(zipEntry, file)
		return err
	})
}

func handleDownload(w http.ResponseWriter, r *http.Request) {
	filename := strings.TrimPrefix(r.URL.Path, "/download/")
	if filename == "" {
		http.Error(w, "文件名不能为空", http.StatusBadRequest)
		return
	}

	filePath := filepath.Join(downloadsDir, filename)
	if _, err := os.Stat(filePath); os.IsNotExist(err) {
		http.Error(w, "文件不存在", http.StatusNotFound)
		return
	}

	w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=%s", filename))
	w.Header().Set("Content-Type", "application/zip")
	http.ServeFile(w, r, filePath)
}

func handleStatus(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json")
	fmt.Fprintf(w, `{"status": "running"}`)
} 